package ko;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;

import android.util.Log;

import ko.util.IFunction;
import ko.util.KEquations;
import ko.util.KPropertyInfoObj;
import ko.util.KTweenObj;
import ko.util.TransitionParam;
import ko.util.Type;

@SuppressWarnings("unused")
public class KTweener {
//	private static Boolean _engineExists = false;
//	private static Boolean _inited = false;
//	private static var _currentTime:Number;
//	private static float _currentTimeFrame;

	private static ArrayList<KTweenObj> _tweenList;
	private static Hashtable<String, IFunction> _transitionList;

	private static float _timeScale = 1; // Time scale (default = 1)

	public KTweener() {
	}

	@SuppressWarnings("unchecked")
	public static Boolean addTween(Object p_scopes,	Object... p_parameters) {//Hashtable<String, Object> p_parameters
		int i;
		String istr;

		ArrayList<Object> rScopes; // List of objects to tween
		if (p_scopes instanceof ArrayList<?>) {// The first argument is an array
			rScopes = (ArrayList<Object>) p_scopes;
		} else {// The first argument(s) is(are) object(s)
			rScopes = new ArrayList<Object>();
			rScopes.add(p_scopes);
		}
		// make properties chain ("inheritance")
		Hashtable<String, Object> p_obj = KTweenObj.makePropertiesChain(p_parameters);

		float rTime =  (p_obj.get("time") == null ? 0.0f : Float.parseFloat(p_obj.get("time").toString())); // Real time
		float rDelay = (p_obj.get("delay") == null ? 0.0f : Float.parseFloat(p_obj.get("delay").toString())); // Real delay

		Hashtable<String, Boolean> restrictedWords = KTweener.getRestrictedWords();

		Hashtable<String, KPropertyInfoObj> rProperties = new Hashtable<String, KPropertyInfoObj>(); // Object
																										// containing
																										// a
																										// list
																										// of
																										// PropertyInfoObj
																										// instances
		Enumeration<String> keys = p_obj.keys();
		while (keys.hasMoreElements()) {
			istr = keys.nextElement();
			if (restrictedWords.get(istr) == null) {
				rProperties.put(istr, new KPropertyInfoObj(Float.NaN, Float.parseFloat(p_obj.get(istr).toString())));
			}
		}

		IFunction rTransition = null; // Real transition

		if (Type.of(p_obj.get("transition")) == "String") { // String parameter, transition names
			String trans = ((String) p_obj.get("transition")).toLowerCase();
			rTransition = _transitionList.get(trans);
		} else {// Proper transition function
			rTransition = (IFunction) p_obj.get("transition");
		}
		// easeoutexpo:easenone:linear
		if (rTransition == null)
			rTransition = _transitionList.get("linear");

		Hashtable<String, KPropertyInfoObj> nProperties;
		KTweenObj nTween;
		int myT;

		for (i = 0; i < rScopes.size(); i++) {
			// Makes a copy of the properties
			nProperties = new Hashtable<String, KPropertyInfoObj>();
			keys = rProperties.keys();
			while (keys.hasMoreElements()) {
				istr = keys.nextElement();
				
				nProperties.put(istr,new KPropertyInfoObj(rProperties.get(istr).valueStart,
									rProperties.get(istr).valueComplete,
									rProperties.get(istr).valueComplete,
									rProperties.get(istr).arrayIndex, 
									rProperties.get(istr).extra,
									rProperties.get(istr).isSpecialProperty,
									rProperties.get(istr).modifierFunction,
									rProperties.get(istr).modifierParameters));
			}

			nTween = new KTweenObj(
			/* scope */			rScopes.get(i),
			/* timeStart */		K._time + ((rDelay * 1000) / _timeScale),
			/* timeComplete */	K._time + (((rDelay * 1000) + (rTime * 1000)) / _timeScale),
			/* useFrames */		false,
			/* transition,p */	rTransition, (TransitionParam) p_obj.get("transitionParams"));

			nTween.properties = nProperties;
			nTween.onComplete = (IFunction) p_obj.get("onComplete");
			nTween.onCompleteParams = (ArrayList<Object>) p_obj.get("onCompleteParams");
			nTween.onCompleteScope = p_obj.get("onCompleteScope");

			_tweenList.add(nTween);

			if (rTime == 0.0f && rDelay == 0.0f) {
				myT = _tweenList.size() - 1;
				updateTweenByIndex(myT);
				removeTweenByIndex(myT);
			}
		}
		return true;
	}

	public static void init() {
		_transitionList = new Hashtable<String, IFunction>();
		KEquations.init();

		startEngine();
	}

	private static void startEngine() {
		_tweenList = new ArrayList<KTweenObj>();
	}

	public static void update() {
//		Boolean hasUpdated = false;
//		hasUpdated = updateTweens();
		updateTweens();
	}

	private static Boolean updateTweens() {
		if (_tweenList.size() == 0)	return false;
		for (int i = 0; i < _tweenList.size(); i++) {
			//_tweenList.get(i) == null || 
			//Log.i(String.valueOf(!_tweenList.get(i).isPaused), "asdfasdf");
			if (!_tweenList.get(i).isPaused) {
				if (!updateTweenByIndex(i))	removeTweenByIndex(i);
				if (_tweenList.get(i) == null) {
					removeTweenByIndex(i, true);
					i--;
				}
			}
		}
		return true;
	}

	private static Boolean removeTweenByIndex(int i, Boolean p_finalRemoval) {
		removeTweenByIndexHelper(i, p_finalRemoval);
		return true;
	}
	private static Boolean removeTweenByIndex(int i) {
		removeTweenByIndexHelper(i, false);
		return true;
	}
	private static Boolean removeTweenByIndexHelper(int i, Boolean p_finalRemoval) {
		_tweenList.set(i, null);
		if (p_finalRemoval)
			_tweenList.remove(i);
		return true;
	}

	static private Boolean updateTweenByIndex(int i) {
		KTweenObj tTweening = _tweenList.get(i);
		if (tTweening == null || tTweening.scope == null) return false;
		
		Boolean isOver = false;		// Whether or not it's over the update time
		
		Object tScope = tTweening.scope;
		
		float nv;					// New value for each property
		float t;					// current time (frames, seconds)
		float b;					// beginning value
		float c;					// change in value
		float d; 					// duration (frames, seconds)
		// var cTime:Number = getCurrentTweeningTime(tTweening);
		float cTime = K._time;
		
		String pName;
		Object eventScope;			// Event scope, used to call functions
		KPropertyInfoObj tProperty;			// Property being checked
		
		float pv = Float.NaN;
		
		Enumeration<String> keys = tTweening.properties.keys();
		while (keys.hasMoreElements()) {// Directly read property
			pName = keys.nextElement();
			try {
				pv = (Float) tScope.getClass().getField(pName).get(tScope);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (NoSuchFieldException e) {
				e.printStackTrace();
			}
			tTweening.properties.get(pName).valueStart = Float.isNaN(pv) ? tTweening.properties.get(pName).valueComplete : pv;
		}
		
		if (cTime >= tTweening.timeComplete) {
			isOver = true;
		}
		keys = tTweening.properties.keys();
		while (keys.hasMoreElements()) {// Directly read property
			pName = keys.nextElement();
			tProperty = tTweening.properties.get(pName);
			if (isOver) {// Tweening time has finished, just set it to the final value
				nv = tProperty.valueComplete;
			} else {
				t = cTime - tTweening.timeStart;
				b = tProperty.valueStart;
				c = tProperty.valueComplete - tProperty.valueStart;
				d = tTweening.timeComplete - tTweening.timeStart;
				nv = Float.parseFloat(tTweening.transition.function(t, b, c, d, tTweening.transitionParams).toString());
			}
			
			try {
				tScope.getClass().getField(pName).set(tScope, nv);
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (NoSuchFieldException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		
		if (isOver && tTweening.onComplete != null) {
			eventScope = (tTweening.onCompleteScope == null) ? tTweening.onCompleteScope : tScope;
			try {
				tTweening.onComplete.function(eventScope, tTweening.onCompleteParams);
			} catch(Exception e) {
				//handleError(tTweening, e4, "onComplete");
			}
		}
		
		return (!isOver);
	}

	public static void registerTransition(String p_name, IFunction p_function) {
		_transitionList.put(p_name, p_function);
	}

	private static Hashtable<String, Boolean> getRestrictedWords() {
		Hashtable<String, Boolean> ht = new Hashtable<String, Boolean>();
		ht.put("time", true);
		ht.put("delay", true);
		ht.put("useFrames", true);
		ht.put("transition", true);
		ht.put("transitionParams", true);
		ht.put("onComplete", true);
		ht.put("onCompleteParams", true);
		return ht;
	}
}
